﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;

namespace Isuka.Reflection
{
    /// <summary>
    /// プロパティコレクション
    /// </summary>
    public class PropertyInfoDictionary : KeyedCollection<string, PropertyInfo>
    {
        public PropertyInfoDictionary(Type type)
        {
            Contract.Requires(type != null);
            Array.ForEach(type.GetProperties(), x => Add(x));
        }

        protected override string GetKeyForItem(PropertyInfo item)
        {
            return item.Name;
        }

        /// <summary>
        /// 属性からプロパティを検索する
        /// </summary>
        /// <typeparam name="TAttribute"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public IEnumerable<PropertyInfo> FindByA<TAttribute>(Func<TAttribute, bool> predicate)
            where TAttribute : Attribute
        {
            Contract.Requires<ArgumentNullException>(predicate != null, "predicate");
            Contract.Ensures(Contract.Result<IEnumerable<PropertyInfo>>() != null);

            return this.Where(x =>
            {
                return
                    x.GetCustomAttributes(typeof(TAttribute), false)
                        .Cast<TAttribute>()
                        .Any(predicate)
                        ;
            });
        }

        /// <summary>
        /// 属性からプロパティを検索し、属性と一緒に返す
        /// </summary>
        /// <typeparam name="TAttribute"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public IEnumerable<Tuple<PropertyInfo, TAttribute>> SelectWithA<TAttribute>(Func<TAttribute, bool> predicate)
            where TAttribute : Attribute
        {
            return this.Select(x =>
            {
                var attr = x.GetCustomAttributes(typeof(TAttribute), false)
                        .Cast<TAttribute>()
                        .FirstOrDefault()
                        ;
                return Tuple.Create(x, attr);
            })
            .Where(x => predicate(x.Item2))
            ;
        }
    }
}
